using System.Collections;
using System.Collections.Generic;
#if TEST
using Xunit;
#endif

namespace Fadd.Parser
{
    /// <summary>
    /// A generated tree with all tokens from the text
    /// </summary>
    public class ParseTree : IEnumerable<Token>
    {
        private readonly Token _root = new Node();
        private Token _current;
        private Token _lastInserted;
        private readonly LinkedList<Token> _trail = new LinkedList<Token>();
        private readonly int _trailLength = 3;

        /// <summary>
        /// Initializes a new instance of the <see cref="ParseTree"/> class.
        /// </summary>
        public ParseTree()
        {
            _current = Root;
        }


        /// <summary>
        /// Initializes a new instance of the <see cref="ParseTree"/> class.
        /// </summary>
        /// <param name="trailLength">Number of items to keep in the <see cref="Trail"/>.</param>
        public ParseTree(int trailLength)
        {
            _trailLength = trailLength;
            _current = Root;
        }


        /// <summary>
        /// Current node
        /// </summary>
        public Token Current
        {
            get
            {
                return _current;
            }
        }

        /// <summary>
        /// Add a new token to the tree.
        /// </summary>
        /// <param name="token"></param>
        public void Add(Token token)
        {
            _lastInserted = token;
            _trail.AddLast(token);
            if (_trail.Count > TrailLength)
                _trail.RemoveFirst();

            _current.Add(token);
        }

        /// <summary>
        /// Get one of the children
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public Token this[int index]
        {
            get { return Root[index]; }
        }

        /// <summary>
        /// Number of root items.
        /// </summary>
        public int Count
        {
            get
            {
                return Root.Count;
            }
        }

        /// <summary>
        /// The node that was inserted last.
        /// </summary>
        public Token LastInserted
        {
            get { return _lastInserted; }
        }

        /// <summary>
        /// A trail with the <see cref="TrailLength"/> number of previously added nodes, independent of the tree/depth.
        /// </summary>
        public LinkedListNode<Token> Trail
        {
            get { return _trail.Last; }
        }


        /// <summary>
        /// Number of items to keep in the trail.
        /// </summary>
        /// <remarks>
        /// Specified in the constructor, default value is 3.
        /// </remarks>
        public int TrailLength
        {
            get { return _trailLength; }
        }

        /// <summary>
        /// Root in the tree.
        /// </summary>
        public Token Root
        {
            get { return _root; }
        }

        /// <summary>
        /// Adds a child node.
        /// </summary>
        /// <param name="token">Token to be added as a child.</param>
        /// <remarks>this method will also set the token as <see cref="Current"/>.</remarks>
        /// todo: remove this one and fix haml
        public void AddChild(Token token)
        {
            _current.Add(token);
            _current = token;
        }
        

        #region Implementation of IEnumerable

        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.Collections.Generic.IEnumerator`1" /> that can be used to iterate through the collection.
        /// </returns>
        /// <filterpriority>1</filterpriority>
        public IEnumerator<Token> GetEnumerator()
        {
            return Root.GetEnumerator();
        }

        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
        /// </returns>
        /// <filterpriority>2</filterpriority>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return Root.GetEnumerator();
        }

        #endregion

        /// <summary>
        /// Next item is a child to the current node.
        /// </summary>
        public void IntoNode()
        {
            _current = _current.LastChild;
        }

        /// <summary>
        /// Go out of one level of nesting.
        /// </summary>
        public void OutOfNode()
        {
            _current = _current.Parent;
        }
        private class Node : Token
        {
            public override int TokenIdentifier
            {
                get { return 0; }
            }
        }
#if TEST
        private class ChildNode1 : Token{
            public override int TokenIdentifier
            {
                get { return 1; }
            }
        }
        private class ChildNode2 : Token
        {
            public override int TokenIdentifier
            {
                get { return 1; }
            }
        }

        [Fact]
        private void TestNesting()
        {
            // build: Node ( child1 ( child2 ) child1 ) node
            Add(new Node());
            IntoNode();
            Add(new ChildNode1());
            IntoNode();
            Add(new ChildNode2());
            Add(new ChildNode2());
            OutOfNode();
            Add(new ChildNode1());
            OutOfNode();
            Add(new Node());

        }
#endif

        /// <summary>
        /// Back to root node.
        /// </summary>
        public void Reset()
        {
            _current = Root;
        }

		/// <summary>
		/// Displays parse tree as a tree.
		/// </summary>
		/// <returns></returns>
        public override string ToString()
        {
            string toString = string.Empty;
            foreach (Token token in _root.Children)
                toString += token.ToString(true);
            return toString;
        }
    }
}
